package xyz.lwm.lazycat.web.handle;

import lombok.SneakyThrows;
import xyz.lwm.lazycat.utility.It;
import xyz.lwm.lazycat.utility.exception.IoRuntimeException;
import xyz.lwm.lazycat.utility.exception.NotFoundException;
import xyz.lwm.lazycat.utility.resource.Resource;
import xyz.lwm.lazycat.utility.scan.ScanUtil;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLConnection;
import java.nio.file.Files;
import java.nio.file.Paths;

import static xyz.lwm.lazycat.utility.PathUtil.normalizePath;
import static xyz.lwm.lazycat.utility.StrPool.STR_SLASH;
import static xyz.lwm.lazycat.web.handle.ContentType.OCTET_STREAM;

/**
 * resource handler, load resource from classpath or external
 *
 * @author lwm
 */
public class ResourceHandler implements Handler {

    private final int contextPathLength;

    private final Resource root;

    public ResourceHandler(String contextPath, String patternDir) {
        this.contextPathLength = (STR_SLASH + normalizePath(contextPath)).length();
        this.root = ScanUtil.scanResources(patternDir).stream().findFirst().orElse(null);
    }

    @SneakyThrows
    @Override
    public void handle(Context ctx) {
        if (root == null) {
            throw new NotFoundException("Not Found");
        }

        final String path = ctx.getPath().substring(contextPathLength);
        Resource resource = root.get(path);
        if (resource.exists()) {
            if (!resource.isDir()) {
                write(ctx, resource);
                if (ctx.isSent()) {
                    return;
                }
            }
        }

        throw new NotFoundException("Not Found");
    }

    private void write(Context ctx, Resource resource) throws IOException {
        InputStream is = resource.getInputStream();
        final long fileSize = is.available();
        final String fileName = resource.getName();
        final String contentType = It.of(getMimeType(fileName))
                .orElse(OCTET_STREAM.toString()).get();

        try (BufferedInputStream bis = new BufferedInputStream(is)) {
            ctx.res().write(bis, (int) fileSize, contentType, fileName);
        }
    }

    private String getMimeType(String filePath) {
        String contentType = URLConnection.getFileNameMap().getContentTypeFor(filePath);
        if (contentType == null) {
            // 补充一些常用的mimeType
            if (filePath.endsWith(".css")) {
                contentType = "text/css";
            } else if (filePath.endsWith(".js")) {
                contentType = "application/x-javascript";
            } else if (filePath.endsWith(".rar")) {
                contentType = "application/x-rar-compressed";
            } else if (filePath.endsWith(".7z")) {
                contentType = "application/x-7z-compressed";
            }
        }

        // 补充
        if (null == contentType) {
            try {
                contentType =  Files.probeContentType(Paths.get(filePath));
            } catch (IOException e) {
                throw new IoRuntimeException(e);
            }
        }

        return contentType;
    }

}
